use async_trait::async_trait;
use base_data::channel::EndReason;
use tools::{error::Error, utils::current_data_time_with_zone, ResultNull};
use tracing::{debug, info};

use crate::{
	business::{
		bus_trait::Business,
		inbound400::context::{BusinessState, InboundRunContext},
	},
	config::business_config,
	db::persistence::InboundDBO,
	link_to_sip::{answer, play_sound_file_to_caller, stop_play_bell},
	CallingLogType,
};

#[derive(Debug, Clone)]
///IVR的实现代码
pub struct IVR;

#[async_trait]
impl Business for IVR {
	type C = InboundRunContext;

	async fn play_back_stop(&self, context: &mut InboundRunContext, _channel_id: u32) -> ResultNull {
		play_sound_file_to_caller(
			context.caller_channel(),
			&context.node_info.conn_begin_bell_path,
		)?;

		Ok(())
	}

	///执行过程当中定时执行调用的检查.可以进行费用\时长等的检查.
	async fn check(&self, context: &mut Self::C) -> ResultNull {
		if context.is_charging() {
			//检查是在等待按键过程中.的时长
			if let Some(b) = context.charge_time.as_ref() {
				let du = current_data_time_with_zone().unix_timestamp() - b.unix_timestamp();
				//按键等待178秒
				if du >= 178 {
					context.business_state = BusinessState::End(EndReason::IvrTalkNocallerd);
				}
			} else {
				context.start_charge();
			}
		}

		Ok(())
	}

	///挂断的业务逻辑
	async fn hangup(
		&self,
		context: &mut Self::C,
		_channel_id: u32,
		_reason: EndReason,
	) -> ResultNull {
		info!("呼叫过程当中主叫挂断");

		context.business_state = BusinessState::End(EndReason::IvrTalkCallerPending);

		Ok(())
	}

	///得到按键的操作.
	async fn get_dtmf(&self, context: &mut Self::C, _id: u32, dtmf: char) -> ResultNull {
		//任何时候收到*号键 返回上层的操作
		if dtmf == '*' {
			debug!("收到*号,清除按键缓冲,并返回上层");
			context.clear_dtmf();
			stop_play_bell(context.caller_channel())?;
			//已经是最上级返回自己
			if !context.node_info.parent_id == 0 {
				context.node_info = InboundDBO::get_node_info(context.node_info.parent_id, context).await?;
			}

			return Err(Error::ReplaceNode);
		}

		//得到第一个按键后关闭放音.
		if context.dtmf.is_empty() {
			stop_play_bell(context.caller_channel())?;
		}

		context.set_dtmf(dtmf);
		info!("收到按键:{}", context.dtmf);

		//按键不够成功返回.不进行操作
		if (context.dtmf.len() as i32) < context.node_info.under_key_len {
			debug!("收到按键,但位数不够,放过,等待下一个");
			return Ok(());
		}

		//得到下级相关节点信息
		let nodes = InboundDBO::get_sub_node_info(context.node_info.node_id, context).await?;

		if nodes.is_empty() {
			InboundDBO::record_calling_log(
				context,
				CallingLogType::GiveACall,
				EndReason::NotSetNode,
				None,
				Some(context.node_info.node_id),
				&current_data_time_with_zone(),
				crate::business::TurnnumCallType::Unknow,
			)
			.await?;

			context.business_state = BusinessState::End(EndReason::NotSetNode);
			return Ok(());
		}

		match nodes.into_iter().find(|item| item.dtfm == context.dtmf) {
			Some(node) => {
				context.node_info = node;
				Err(Error::ReplaceNode)
			}
			None => {
				//走到这里是一定是按键出错了..放音,清除所有按键,等待再次按键
				play_sound_file_to_caller(
					context.caller_channel(),
					business_config().not_found_node_bell_path,
				)?;
				context.clear_dtmf();
				Ok(())
			}
		}
	}

	///结束的操作,用来计费,上传等等.在通道退出前调用
	async fn closeing(&self, context: &mut Self::C) -> ResultNull {
		//计费
		InboundDBO::record_and_charge(
			context,
			"",
			&context.rec_file_name,
			EndReason::IvrTalkCallerPending,
			context.node_info.node_id,
			&context.node_info.dtfm,
			"",
		)
		.await?;

		Ok(())
	}
	async fn start_call(&self, context: &mut Self::C) -> ResultNull {
		debug!("开始启动IVR节点...当前参数:{:?}", context);

		//接通主叫.因为这是ivr,一定要接通才行.
		answer(context.caller_channel())?;
		context.start_charge();

		play_sound_file_to_caller(
			context.caller_channel(),
			&context.node_info.conn_begin_bell_path,
		)?;
		context.business_state = BusinessState::WaitDtmf;

		Ok(())
	}

	///通道已经绑定的消息处理
	async fn bridge(&self, _context: &mut InboundRunContext) -> ResultNull {
		Err(Error::LogicalError("IVR不应该出现接通操作"))
	}
}
